/**
 * 
 */
package org.epanetgrid.otimizacao.tui;

import java.io.File;
import java.util.LinkedList;
import java.util.List;

import org.epanetgrid.otimizacao.OtimizadorEvent;
import org.epanetgrid.otimizacao.OtimizadorObserver;
import org.epanetgrid.otimizacao.ag.ViaveisSelector;
import org.epanetgrid.otimizacao.util.OtimizacaoUtil;
import org.epanetgrid.relatorios.common.ExportXLS;
import org.jheuristics.Individual;
import org.jheuristics.ga.DefaultEvolver;
import org.jheuristics.ga.DefaultPopulation;
import org.jheuristics.ga.GAStatus;
import org.jheuristics.ga.Population;
import org.jheuristics.ga.operators.selectors.DefaultRepresentativosSelector;
import org.jheuristics.ga.operators.selectors.RepresentativosSelector;
import org.jheuristics.ga.transformations.DoubleFitnessToDoubleTransformation;
import org.jheuristics.ga.transformations.FitnessToDoubleTransformation;
import org.jheuristics.util.BitString;
import org.joda.time.DateTime;

public class StatusOtimizacaoXLS implements OtimizadorObserver {

	private int geracao;
	private boolean stopped;
	private Population currentPopulation;
	private int objetivos;
	private File arquivo;
	private ExportXLS exportXLS;
	private int representativas;

	public StatusOtimizacaoXLS(int objetivos, int representativas, File arquivo) {
		this.objetivos = objetivos;
		this.representativas = representativas;
		this.geracao = 0;
		this.stopped = false;
		this.arquivo = arquivo;
		this.exportXLS = new ExportXLS(objetivos);
	}

	public synchronized void update(OtimizadorEvent event) {

		GAStatus status = null;
		
		if (event.getData() instanceof GAStatus) {
			status = (GAStatus) event.getData();
		}
		
		switch (event.getType()) {

		case OtimizadorEvent.INICIO:
			exportXLS.inserirTempo("Inicio", new DateTime()
					.toString("dd/MM/yyyy"), new DateTime()
					.toString("HH:mm:ss"));
			break;
		case OtimizadorEvent.PAUSE:
			break;
		case OtimizadorEvent.FINAL:
		case OtimizadorEvent.TEMPO_LIMITE:
		case OtimizadorEvent.NAO_CONVERGENCIA:
			exportXLS.inserirTempo("Final", new DateTime()
					.toString("dd/MM/yyyy"), new DateTime()
					.toString("HH:mm:ss"));
			exportXLS.exportResults(this.arquivo);
			// TODO LOGAR POPULACAO
			break;
		case OtimizadorEvent.ERRO:
			break;
		case OtimizadorEvent.PASSO:
			if (stopped) {
				return;
			}

			currentPopulation = status.getCurrentPopulation();

			geracao++;

			exportXLS.inserirTempo("Geracao " + geracao, new DateTime()
					.toString("dd/MM/yyyy"), new DateTime()
					.toString("HH:mm:ss"));

			exportXLS.resetPopulacao();

			exportXLS.resetPopulacaoRed();

			int solucao = 1;
			
			for (Individual individual : status.getCurrentPopulation()
					.toIndividualArray()) {
				LinkedList app = (LinkedList) individual.getDataApplication();
				Double fo1 = 0.0, fo2 = 0.0, fo3 = 0.0;
				switch (objetivos) {
				case 3:
					if (app.get(2) instanceof Integer) {
						fo3 = new Double((double) (Integer) app.get(2));
					} else {
						fo3 = (Double) app.get(2);
					}
				case 2:
					if (app.get(1) instanceof Integer) {
						fo2 = new Double((double) (Integer) app.get(1));
					} else {
						fo2 = (Double) app.get(1);
					}
				case 1:
					if (app.get(0) instanceof Integer) {
						fo1 = new Double((double) (Integer) app.get(0));
					} else {
						fo1 = (Double) app.get(0);
					}
					break;
				default:
					break;
				}
				long pf = ((Long) app.get(objetivos));
				String cromossomo = OtimizacaoUtil.gensToString((List<BitString>) individual.getGens());
				exportXLS.inserirPopulacao(solucao, fo1, fo2, fo3, pf,
						cromossomo);
			}
			
			solucao = 1;
			
			String[] descricoes = ((DefaultEvolver)event.getSource().getEvolver()).getDescricoes();
			
			FitnessToDoubleTransformation[] transformations = new DoubleFitnessToDoubleTransformation[descricoes.length - 3];
			
			for (int i = 0; i < transformations.length; i++) {
				transformations[i] = new DoubleFitnessToDoubleTransformation();
			}
			
			Individual[] representativos = new RepresentativosSelector(new DefaultRepresentativosSelector(transformations.length)).select(status.getCurrentPopulation(), representativas, null, null);

			for (Individual individual : representativos) {
				LinkedList app = (LinkedList) individual.getDataApplication();
				Double fo1 = 0.0, fo2 = 0.0, fo3 = 0.0;
				switch (objetivos) {
				case 3:
					if (app.get(2) instanceof Integer) {
						fo3 = new Double((double) (Integer) app.get(2));
					} else {
						fo3 = (Double) app.get(2);
					}
				case 2:
					if (app.get(1) instanceof Integer) {
						fo2 = new Double((double) (Integer) app.get(1));
					} else {
						fo2 = (Double) app.get(1);
					}
				case 1:
					if (app.get(0) instanceof Integer) {
						fo1 = new Double((double) (Integer) app.get(0));
					} else {
						fo1 = (Double) app.get(0);
					}
					break;
				default:
					break;
				}
				long pf = ((Long) app.get(objetivos));
				String cromossomo = OtimizacaoUtil.gensToString((List<BitString>) individual.getGens());
				exportXLS.inserirPopulacaoRed(solucao++, fo1, fo2, fo3, pf,
						cromossomo);
			}

			if (objetivos > 1) {
				List<Comparable> minimos = new LinkedList<Comparable>();

				Individual[] individuos = status.getCurrentPopulation()
						.toIndividualArray();

				for (int i = 0; i < objetivos; i++) {
					int index = 0;

					Comparable min = (Comparable) ((List) individuos[0]
							.getDataApplication()).get(i);

					for (int j = 1; j < individuos.length; j++) {
						Comparable value = (Comparable) ((List) individuos[j]
								.getDataApplication()).get(i);

						if (min.compareTo(value) > 0) {
							index = j;
							min = value;
						}
					}
					minimos.add(individuos[index]);
				}

				int objetivoAtual = 1;
				for (Comparable minimo : minimos) {
					Individual individual = (Individual) minimo;
					LinkedList app = (LinkedList) individual
							.getDataApplication();
					Double fo1 = 0.0, fo2 = 0.0, fo3 = 0.0;
					switch (objetivos) {
					case 3:
						if (app.get(2) instanceof Integer) {
							fo3 = new Double((double) (Integer) app.get(2));
						} else {
							fo3 = (Double) app.get(2);
						}
					case 2:
						if (app.get(1) instanceof Integer) {
							fo2 = new Double((double) (Integer) app.get(1));
						} else {
							fo2 = (Double) app.get(1);
						}
					case 1:
						if (app.get(0) instanceof Integer) {
							fo1 = new Double((double) (Integer) app.get(0));
						} else {
							fo1 = (Double) app.get(0);
						}
						break;
					default:
						break;
					}
					long pf = ((Long) app.get(objetivos));
					String cromossomo = OtimizacaoUtil.gensToString((List<BitString>) individual.getGens());
					if (objetivoAtual == 1) {
						exportXLS.inserirFO1Global(geracao, fo1, fo2, fo3, pf,
								cromossomo);
						objetivoAtual++;
					} else if (objetivoAtual == 2) {
						exportXLS.inserirFO2Global(geracao, fo1, fo2, fo3, pf,
								cromossomo);
						objetivoAtual++;
					} else if (objetivoAtual == 3) {
						exportXLS.inserirFO3Global(geracao, fo1, fo2, fo3, pf,
								cromossomo);
						objetivoAtual++;
					}
				}

				// recuperando os minimos viaveis
				List<Comparable> minimosViaveis = new LinkedList<Comparable>();

				Population viaveis = new DefaultPopulation();
				viaveis.addIndividuals(new ViaveisSelector().select(status
						.getCurrentPopulation(), 0, null, null));

				Individual[] bestObjectivesViaveis = viaveis
						.toIndividualArray();

				if (bestObjectivesViaveis.length != 0) {
					for (int i = 0; i < objetivos; i++) {
						int index = 0;
						Comparable min = (Comparable) (((List) bestObjectivesViaveis[index]
								.getDataApplication()).get(i));
						for (int j = 1; j < bestObjectivesViaveis.length; j++) {
							Comparable value = (Comparable) (((List) bestObjectivesViaveis[j]
									.getDataApplication()).get(i));
							if (min.compareTo(value) > 0) {
								index = j;
								min = value;
							}
						}
						minimosViaveis.add(bestObjectivesViaveis[index]);
					}

				}

				objetivoAtual = 1;

				if (!minimosViaveis.isEmpty()) {
					for (Comparable minimo : minimos) {
						Individual individual = (Individual) minimo;
						LinkedList app = (LinkedList) individual
								.getDataApplication();
						Double fo1 = 0.0, fo2 = 0.0, fo3 = 0.0;
						switch (objetivos) {
						case 3:
							if (app.get(2) instanceof Integer) {
								fo3 = new Double((double) (Integer) app.get(2));
							} else {
								fo3 = (Double) app.get(2);
							}
						case 2:
							if (app.get(1) instanceof Integer) {
								fo2 = new Double((double) (Integer) app.get(1));
							} else {
								fo2 = (Double) app.get(1);
							}
						case 1:
							if (app.get(0) instanceof Integer) {
								fo1 = new Double((double) (Integer) app.get(0));
							} else {
								fo1 = (Double) app.get(0);
							}
							break;
						default:
							break;
						}
						long pf = ((Long) app.get(objetivos));
						String cromossomo = OtimizacaoUtil.gensToString((List<BitString>) individual.getGens());
						if (objetivoAtual == 1) {
							exportXLS.inserirFO1Viavel(geracao, fo1, fo2, fo3,
									pf, cromossomo);
							objetivoAtual++;
						} else if (objetivoAtual == 2) {
							exportXLS.inserirFO2Viavel(geracao, fo1, fo2, fo3,
									pf, cromossomo);
							objetivoAtual++;
						} else if (objetivoAtual == 3) {
							exportXLS.inserirFO3Viavel(geracao, fo1, fo2, fo3,
									pf, cromossomo);
							objetivoAtual++;
						}
					}
				}
			} else {
				Individual individual = status.getCurrentPopulation()
						.getIndividual(0);

				LinkedList app = (LinkedList) individual.getDataApplication();

				Double fo1 = 0.0, fo2 = 0.0, fo3 = 0.0;


				if (app.get(0) instanceof Integer) {
					fo1 = new Double((double) (Integer) app.get(0));
				} else {
					fo1 = (Double) app.get(0);
				}
				long pf = ((Long) app.get(1));
				String cromossomo = OtimizacaoUtil.gensToString((List<BitString>) individual.getGens());
				exportXLS.inserirFO1Global(geracao, fo1, fo2, fo3, pf,
						cromossomo);

			}
			exportXLS.exportResults(this.arquivo);
			break;
		}
	}

}
